﻿/**
 * @file lv_demo_long.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "../../lv_demo.h"
#include <stdlib.h>

/*********************
 *      DEFINES
 *********************/
#define GAME_POS_X    80     // 游戏区域位置
#define GAME_POS_Y    115    // 游戏区域位置
#define GAME_WIDTH    640    // 游戏区域宽
#define GAME_HIGH     291     // 游戏区域高

#define BIRD_POS_MAX_X    35
#define BIRD_POS_MIN_Y    25
#define BIRD_POS_MAX_Y    195
#define BIRD_WIDTH     birds[0].header.w

#define COIN_FLAG      0
#define SPHERE_FLAG    1

/**********************
 *      TYPEDEFS
 **********************/
typedef struct
{
    int16_t offset;
    int16_t w;
    lv_obj_t *obj;
}anim_obj_t;

typedef struct
{
    lv_obj_t* obj;
    int16_t index;
    lv_img_dsc_t* img;
    lv_timer_t* timer;
    uint8_t flag;
    uint32_t speed;
}coin_gif_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static inline void game_start(void);
static void back_anim_create(lv_obj_t* parent, const void* src, anim_obj_t* anim_data, uint32_t period, int16_t offset_y);
static void coin_gif_create(lv_obj_t* parent, coin_gif_t* coin_gif, int16_t offset_x, int16_t offset_y);
static void game_start_anim(lv_anim_t* anim, lv_obj_t* obj, uint32_t duration, uint16_t delay, void* data);
static uint32_t lv_anim_time_to_speed(uint32_t duration, int32_t start, int32_t end);
static void bird_gif_init(lv_img_dsc_t* bird);
static void coin_gif_init(lv_img_dsc_t* coin);
static void background_anim_cb(lv_timer_t* timer);
static void bird_gif_cb(lv_timer_t* timer);
static void coin_gif_cb(lv_timer_t* timer);
static void bird_rise_cb(lv_event_t* data);
static void bird_start_ecptoma_cb(lv_event_t* data);
static void bird_ecptoma_cb(lv_timer_t* timer);
static void coin_sphere_gen_cb(lv_timer_t* timer);
static uint32_t coin_sphere_anim(lv_anim_t* anim, coin_gif_t* coin_gif, uint32_t duration, uint16_t w);
static void del_coin_gif_cb(lv_anim_t* anim);
static void coin_generate(void);
static void sphere_generate(void);
static void location_moving_cb(lv_anim_t* anim, int32_t pos);
static uint8_t is_collision_bird(int32_t x, int32_t y, int32_t w, int32_t h);
static void game_start_cb(lv_anim_t* anim);
static void restart_game_cb(lv_anim_t* anim);
static void game_over(uint32_t speed);

/**********************
 *  EXTERN VARIABLES
 **********************/
LV_IMG_DECLARE(collectible_item_00);
LV_IMG_DECLARE(collectible_item_01);
LV_IMG_DECLARE(collectible_item_02);
LV_IMG_DECLARE(collectible_item_03);
LV_IMG_DECLARE(collectible_item_04);
LV_IMG_DECLARE(collectible_item_05);
LV_IMG_DECLARE(collectible_item_06);
LV_IMG_DECLARE(collectible_item_07);
LV_IMG_DECLARE(collectible_item_08);
LV_IMG_DECLARE(collectible_item_09);
LV_IMG_DECLARE(game_2d_frame_background);
LV_IMG_DECLARE(game2d_background);
LV_IMG_DECLARE(game2d_ground);
LV_IMG_DECLARE(game2d_topLeaves);
LV_IMG_DECLARE(game2d_TreeTops);
LV_IMG_DECLARE(sphere1);
LV_IMG_DECLARE(sprite_00);
LV_IMG_DECLARE(sprite_01);
LV_IMG_DECLARE(sprite_02);
LV_IMG_DECLARE(sprite_03);
LV_IMG_DECLARE(sprite_04);
LV_IMG_DECLARE(sprite_05);
LV_IMG_DECLARE(sprite_06);
LV_IMG_DECLARE(sprite_07);
LV_IMG_DECLARE(sprite_08);
LV_IMG_DECLARE(sprite_09);

/**********************
 *  STATIC VARIABLES
 **********************/
static lv_obj_t* background;
static lv_obj_t* bird_obj;
static lv_img_dsc_t birds[10];
static lv_img_dsc_t coins[10];
static lv_obj_t* game_area;
static lv_timer_t* bird_ecptoma;
static lv_obj_t* frame_background;
static lv_obj_t* integral_text;
static uint32_t integral_val = 0;
static lv_timer_t* coin_sphere_gen_timer;
static lv_anim_t start_anim;
static uint8_t game_over_flag = false;
static lv_obj_t* screen;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_demo_bird_eat_eoin(void)
{
    static lv_coord_t column_dsc[] = { 160, 160, 160, LV_GRID_TEMPLATE_LAST };
    static lv_coord_t row_dsc[]    = { 120, 120, 120, 120, LV_GRID_TEMPLATE_LAST };
    static lv_style_t cat_style;
    static lv_style_t hole_style;
    static anim_obj_t anim_background;
    static anim_obj_t anim_ground_bottom;
    static anim_obj_t anim_top_leaves;
    static anim_obj_t anim_tree_tops;
    screen = lv_scr_act();

    bird_gif_init(birds);
    coin_gif_init(coins);

    /* 设置桌面背景 */
    frame_background = lv_img_create(screen);
    lv_img_set_src(frame_background, &game_2d_frame_background);

    game_area = lv_label_create(screen);
    lv_label_set_text(game_area, "");
    lv_obj_set_size(game_area, GAME_WIDTH, GAME_HIGH);
    lv_obj_set_pos(game_area, GAME_POS_X, GAME_POS_Y);

    integral_text = lv_label_create(screen);
    lv_obj_align_to(integral_text, game_area, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, 0);
    lv_label_set_recolor(integral_text, true);
    lv_label_set_text_fmt(integral_text, "#ffffff integral:%d#", integral_val);

    back_anim_create(game_area, &game2d_background, &anim_background, 60, 21);
    back_anim_create(game_area, &game2d_ground, &anim_ground_bottom, 5, 208);
    back_anim_create(game_area, &game2d_TreeTops, &anim_tree_tops, 40, 18);
    
    bird_obj = lv_img_create(game_area);
    lv_img_set_src(bird_obj, &birds[0]);
    lv_obj_set_pos(bird_obj, BIRD_POS_MAX_X, BIRD_POS_MAX_Y);
    lv_timer_create(bird_gif_cb, 100, birds);

    bird_ecptoma = lv_timer_create(bird_ecptoma_cb, 1, NULL);

    back_anim_create(game_area, &game2d_topLeaves, &anim_top_leaves, 5, 0);

    coin_sphere_gen_timer = lv_timer_create(coin_sphere_gen_cb, 2000, NULL);
    lv_timer_pause(coin_sphere_gen_timer);
    game_start();
}

static inline void game_start(void)
{
    game_start_anim(&start_anim, bird_obj, 2000, 200, coin_sphere_gen_timer);
}

static void back_anim_create(lv_obj_t* parent, const void* src, anim_obj_t* anim_data, uint32_t period, int16_t offset_y)
{
    lv_obj_t* back_obj = lv_img_create(parent);
    lv_img_set_src(back_obj, src);
    lv_obj_set_size(back_obj, GAME_WIDTH, ((lv_img_dsc_t *)src)->header.h);
    lv_obj_set_pos(back_obj, 0, offset_y);
    anim_data->obj = back_obj;
    anim_data->offset = 0;
    anim_data->w = ((lv_img_dsc_t*)src)->header.w;
    lv_timer_create(background_anim_cb, period, anim_data);
}

static void coin_gif_create(lv_obj_t* parent, coin_gif_t *coin_gif, int16_t offset_x, int16_t offset_y)
{
    coin_gif->obj = lv_img_create(parent);
    lv_img_set_src(coin_gif->obj, &coins[0]);
    lv_obj_set_pos(coin_gif->obj, offset_x, offset_y);
    coin_gif->img = coins;
    coin_gif->index = 0;
    coin_gif->flag = COIN_FLAG;
    coin_gif->timer = lv_timer_create(coin_gif_cb, 100, coin_gif);
}

static void sphere_create(lv_obj_t* parent, coin_gif_t* coin_gif, int16_t offset_x, int16_t offset_y)
{
    coin_gif->obj = lv_img_create(parent);
    lv_img_set_src(coin_gif->obj, &sphere1);
    lv_obj_set_pos(coin_gif->obj, offset_x, offset_y);
    coin_gif->img = NULL;
    coin_gif->index = 0;
    coin_gif->flag = SPHERE_FLAG;
    coin_gif->timer = NULL;
}

static void game_start_anim(lv_anim_t* anim, lv_obj_t *obj, uint32_t duration, uint16_t delay, void *data)
{
    /* INITIALIZE AN ANIMATION
     *-----------------------*/
    lv_anim_init(anim);

    /* MANDATORY SETTINGS
     *------------------*/

     /*Set the "animator" function*/
    lv_anim_set_exec_cb(anim, (lv_anim_exec_xcb_t)lv_obj_set_x);

    /*Set the "animator" function*/
    lv_anim_set_var(anim, obj);

    /*Length of the animation [ms]*/
    lv_anim_set_time(anim, duration);
    lv_anim_set_delay(anim, delay);

    /*Set start and end values. E.g. 0, 150*/
    lv_obj_set_y(obj, BIRD_POS_MAX_Y);
    lv_anim_set_values(anim, -1 * BIRD_WIDTH, BIRD_POS_MAX_X);

    lv_anim_set_ready_cb(anim, game_start_cb);
    lv_anim_set_user_data(anim, data);

    /* START THE ANIMATION
    *------------------*/
    lv_anim_start(anim);                             /*Start the animation*/
}

static uint32_t lv_anim_time_to_speed(uint32_t duration, int32_t start, int32_t end)
{
    uint32_t d = LV_ABS(start - end);
    uint32_t speed = (d * 1000) / duration;

    if (speed == 0) {
        speed++;
    }

    return speed;
}

static void game_over_anim(lv_anim_t* anim, lv_obj_t* obj, uint32_t speed,void* data)
{
    /* INITIALIZE AN ANIMATION
     *-----------------------*/
    lv_anim_init(anim);

    /* MANDATORY SETTINGS
     *------------------*/

     /*Set the "animator" function*/
    lv_anim_set_exec_cb(anim, (lv_anim_exec_xcb_t)lv_obj_set_x);

    /*Set the "animator" function*/
    lv_anim_set_var(anim, obj);

    /*Length of the animation [ms]*/
    lv_anim_set_time(anim, lv_anim_speed_to_time(speed, BIRD_POS_MAX_X, -1 * BIRD_WIDTH));

    /*Set start and end values. E.g. 0, 150*/
    lv_anim_set_values(anim, BIRD_POS_MAX_X, -1 * BIRD_WIDTH);

    lv_anim_set_ready_cb(anim, restart_game_cb);
    lv_anim_set_user_data(anim, data);

    /* START THE ANIMATION
    *------------------*/
    lv_anim_start(anim);                             /*Start the animation*/
}

static void bird_gif_init(lv_img_dsc_t* bird)
{
    birds[0] = sprite_00;
    birds[1] = sprite_01;
    birds[2] = sprite_02;
    birds[3] = sprite_03;
    birds[4] = sprite_04;
    birds[5] = sprite_05;
    birds[6] = sprite_06;
    birds[7] = sprite_07;
    birds[8] = sprite_08;
    birds[9] = sprite_09;
}

static void coin_gif_init(lv_img_dsc_t* coin)
{
    coins[0] = collectible_item_00;
    coins[1] = collectible_item_01;
    coins[2] = collectible_item_02;
    coins[3] = collectible_item_03;
    coins[4] = collectible_item_04;
    coins[5] = collectible_item_05;
    coins[6] = collectible_item_06;
    coins[7] = collectible_item_07;
    coins[8] = collectible_item_08;
    coins[9] = collectible_item_09;
}

static uint8_t srand_0_to_9(void)
{
    uint32_t tmp = 0;
    uint32_t tick = lv_tick_get();
    srand(lv_tick_get());
    // while (lv_tick_get() - tick < 2);
    tmp = rand();
    return tmp % 10;
}

static uint8_t srand_0_to_2(void)
{
    uint32_t tmp = 0;
    uint32_t tick = lv_tick_get();
    srand(lv_tick_get());
    while (lv_tick_get() - tick < 2);
    while (true)
    {
        tmp = rand();
        if ((tmp % 10) % 3 < 3)
        {
            return (tmp % 10) % 3;
        }
    }
}

static uint8_t srand_0_to_3(void)
{
    uint32_t tmp = 0;
    uint32_t tick = lv_tick_get();
    srand(lv_tick_get());
    while (lv_tick_get() - tick < 2);
    while (true)
    {
        tmp = rand();
        if ((tmp % 10) % 4 < 4)
        {
            return (tmp % 10) % 3;
        }
    }
}

static uint32_t srand_movement_speed(void)
{
    uint32_t tmp = 0;
    uint32_t tick = lv_tick_get();
    srand(tick);
    while (lv_tick_get() - tick <  2);
    tmp = rand() & 0x3FF;
    return tmp * 3 + 2000;
}

static uint32_t srand_pos_y(void)
{//74-243
    uint32_t tmp = 0;
    uint32_t tick = lv_tick_get();
    srand(tick);
    while (lv_tick_get() - tick < 2);
    tmp = rand() & 0xFF;
    return tmp + 74;
}

static void background_anim_cb(lv_timer_t* timer)
{
    anim_obj_t* anim_obj = timer->user_data;
    anim_obj->offset -= 3;
    if (anim_obj->offset < -1 * anim_obj->w)
    {
        anim_obj->offset = 0;
    }
    lv_img_set_offset_x(anim_obj->obj, anim_obj->offset);
}

static void bird_gif_cb(lv_timer_t* timer)
{
    static uint8_t index = 0;
    index = ++index % 10;
    lv_img_set_src(bird_obj, ((lv_img_dsc_t*)(timer->user_data)) + index);
}

static void coin_gif_cb(lv_timer_t* timer)
{
    coin_gif_t *coin = (coin_gif_t*)(timer->user_data);
    coin->index = ++(coin->index) % 10;
    lv_img_set_src(coin->obj, coin->img + coin->index);
}

static void bird_rise_cb(lv_event_t* data)
{
    int16_t pos_y = lv_obj_get_y(bird_obj);
    int16_t pos_x = lv_obj_get_x(bird_obj);

    pos_y -= 2;
    pos_y = pos_y < BIRD_POS_MIN_Y ? BIRD_POS_MIN_Y : pos_y;
    lv_obj_set_pos(bird_obj, pos_x, pos_y);
    lv_timer_pause(bird_ecptoma);
}

static void bird_start_ecptoma_cb(lv_event_t* data)
{
    lv_timer_resume(bird_ecptoma);
}

static void bird_ecptoma_cb(lv_timer_t* timer)
{
    int16_t pos_y = lv_obj_get_y(bird_obj);
    int16_t pos_x = lv_obj_get_x(bird_obj);

    pos_y += 3;
    if (pos_y < BIRD_POS_MAX_Y)
    {
        lv_obj_set_pos(bird_obj, pos_x, pos_y);
    }
    else
    {
        lv_obj_set_pos(bird_obj, pos_x, BIRD_POS_MAX_Y);
        lv_timer_pause(bird_ecptoma);
    }
}

static void coin_sphere_gen_cb(lv_timer_t* timer)
{
    coin_generate();
    sphere_generate();
}   

static uint32_t coin_sphere_anim(lv_anim_t* anim, coin_gif_t* coin_gif, uint32_t duration, uint16_t w)
{
    /* INITIALIZE AN ANIMATION
     *-----------------------*/
    lv_anim_init(anim);

    /* MANDATORY SETTINGS
     *------------------*/

     /*Set the "animator" function*/
    lv_anim_set_custom_exec_cb(anim, (lv_anim_exec_xcb_t)location_moving_cb);

    /*Set the "animator" function*/
    //lv_anim_set_var(anim, coin_gif->obj);

    /*Length of the animation [ms]*/
    lv_anim_set_time(anim, duration);

    /*Set start and end values. E.g. 0, 150*/
    lv_anim_set_values(anim, GAME_WIDTH + w, -w);

    lv_anim_set_ready_cb(anim, del_coin_gif_cb);
    lv_anim_set_user_data(anim, coin_gif);

    /* START THE ANIMATION
    *------------------*/
    lv_anim_start(anim);                             /*Start the animation*/

    return lv_anim_time_to_speed(duration, GAME_WIDTH + w, -w);
}

static void del_coin_gif_cb(lv_anim_t* anim)
{
    coin_gif_t* coin_gif = (coin_gif_t*)(anim->user_data);
    if (coin_gif->timer)
        lv_timer_del(coin_gif->timer);
    lv_anim_del(anim, NULL);
    lv_obj_del(coin_gif->obj);
    lv_mem_free(coin_gif);
}

static void coin_generate(void)
{
    uint32_t duration = srand_movement_speed();
    int16_t y = srand_pos_y();

    if (y > 240)
        return;

    lv_anim_t* anim = lv_mem_alloc(sizeof(lv_anim_t));
    coin_gif_t* coin_gif = lv_mem_alloc(sizeof(coin_gif_t));
    LV_ASSERT_MALLOC(anim);
    LV_ASSERT_MALLOC(coin_gif);

    if (anim && coin_gif)
    {
        coin_gif_create(game_area, coin_gif, 0, y);
        coin_gif->speed = coin_sphere_anim(anim, coin_gif, duration, coins[0].header.w);
        coin_gif->flag = COIN_FLAG;
        lv_mem_free(anim);
    }
    else
    {
        if (anim)
            lv_mem_free(anim);
        if (coin_gif)
            lv_mem_free(coin_gif);
    }
}

static void sphere_generate(void)
{
    uint32_t duration = srand_movement_speed();
    int16_t y = srand_pos_y();

    if (y > 240)
        return;

    lv_anim_t* anim = lv_mem_alloc(sizeof(lv_anim_t));
    coin_gif_t* sphere = lv_mem_alloc(sizeof(coin_gif_t));
    LV_ASSERT_MALLOC(anim);
    LV_ASSERT_MALLOC(sphere);

    if (anim && sphere)
    {
        sphere_create(game_area, sphere, 0, y);
        sphere->speed = coin_sphere_anim(anim, sphere, duration, coins[0].header.w);
        sphere->flag = SPHERE_FLAG;
        lv_mem_free(anim);
    }
    else
    {
        if (anim)
            lv_mem_free(anim);
        if (sphere)
            lv_mem_free(sphere);
    }
}

static void location_moving_cb(lv_anim_t* anim, int32_t pos)
{
    coin_gif_t* coin_gif = (coin_gif_t*)lv_anim_get_user_data(anim);
    lv_obj_t* obj = coin_gif->obj;
    int32_t x, y, w, h;
    
    lv_obj_set_x(obj, pos);

    x = lv_obj_get_x(obj);
    y = lv_obj_get_y(obj);
    w = lv_obj_get_width(obj);
    h = lv_obj_get_height(obj);
    
    if (false == game_over_flag && is_collision_bird(x, y, w, h))
    {
        if (COIN_FLAG == coin_gif->flag)
        {
            lv_label_set_text_fmt(integral_text, "#ffffff integral:%d#", ++integral_val);
            del_coin_gif_cb(anim);
        }
        else if (SPHERE_FLAG == coin_gif->flag)
        {
            game_over(coin_gif->speed);
        }
    }
}

static uint8_t is_collision_bird(int32_t x, int32_t y, int32_t w, int32_t h)
{
    int32_t bird_x = lv_obj_get_x(bird_obj);
    int32_t bird_y = lv_obj_get_y(bird_obj);
    int32_t bird_w = lv_obj_get_width(bird_obj);
    int32_t bird_h = lv_obj_get_height(bird_obj);
    if (x > bird_x && x < bird_x + bird_w)
    {
        if ((y > bird_y && y < bird_y + bird_h) || ((y + h)  > bird_y && (y + h) < (bird_y + bird_h)))
        {
            return true;
        }
    }
    return false;
}

static void game_start_cb(lv_anim_t* anim)
{
    lv_timer_t* timer = (lv_timer_t*)lv_anim_get_user_data(anim);
    lv_timer_resume(timer);
    game_over_flag = false;
    /* 允许点击屏幕 */
    lv_obj_add_event_cb(screen, bird_rise_cb, LV_EVENT_PRESSING, NULL);
    lv_obj_add_event_cb(screen, bird_start_ecptoma_cb, LV_EVENT_RELEASED, NULL);
}

static void restart_game_cb(lv_anim_t* anim)
{
    game_start();
}

static void game_over(uint32_t speed)
{
    lv_anim_t anim_tmp;
    integral_val = 0;
    lv_label_set_text_fmt(integral_text, "#ffffff integral:%d#", integral_val);
    lv_timer_pause(coin_sphere_gen_timer);
    lv_timer_pause(bird_ecptoma);
    game_over_flag = true;
    game_over_anim(&anim_tmp, bird_obj, speed, NULL);
    /* 不能点击屏幕 */
    lv_obj_remove_event_cb(screen, bird_rise_cb);
    lv_obj_remove_event_cb(screen, bird_start_ecptoma_cb);
}
